package org.ccay.core.orm.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Named;

import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.ibatis.session.SqlSessionFactory;
import org.ccay.core.base.FilterVO;
import org.ccay.core.base.PageVO;
import org.ccay.core.ioc.Ccay;
import org.ccay.core.ioc.CcayApplicationContext;
import org.ccay.core.log.ILogger;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.orm.DBType;
import org.ccay.core.orm.ITagHandler;
import org.springframework.dao.InvalidDataAccessResourceUsageException;

/**
 * 解析&lt;addFilter &gt;自定义标签 <addFilter prepend ="and (" suffix=")"> <filter
 * name="name" field="name" dataType="VARCHAR" prepend ="or" suffix=")"> . . .
 * </addFilter>
 * 
 * 
 * @since 2013-07-16
 */
@Named("addFilterTagHandler")
public class AddFilterTagHandler implements ITagHandler {
	
	private String databaseId;
	
	private static final ILogger log = CcayLoggerFactory
			.getLogger(AddFilterTagHandler.class);

	/*
	 * 处理&lt;addFilter &gt;标签,
	 * 
	 * @see org.ccay.orm.ITagHandler#handle(org.w3c.dom.Element,
	 * java.lang.String, java.lang.Object)
	 */
	public String handle(Element ele, String tag, Object filterVOList) {
		if(null == databaseId){
			databaseId = Ccay.getContext().getBean(SqlSessionFactory.class).getConfiguration().getDatabaseId();
		}
		
		// 获取addFilter tag子节点列表
		NodeList addFilterTag = ele.getElementsByTagName("addFilter");

		// 获取待过滤的信息列表
		List<FilterVO> filterList = this.findFilter(filterVOList);
		
		//如果前端和xml配置文件中没有给出相关的数据，直接返回，不构建SQL
		if (filterList.isEmpty() || null == addFilterTag) {
			log.debug("FilterVO no data or addFilter tag have no child node.");
			return "";
		}
		/**
		 * 获取xml文件的map对象
		 */
		Map<String, NamedNodeMap> filterAttrMap = getFilterAttrMap(ele);
		String addFilterPrepend = ele.getAttribute("prepend");
		
		//生成最终要执行的SQL片段
		StringBuilder sql = new StringBuilder("");
		boolean first = true;
		NamedNodeMap filterInfo = null;
		String tempFilterName = null;
		Map<String, Object> paramMap =  null;
		for (FilterVO filter : filterList) {
			filterInfo = filterAttrMap.get(filter.getFn());
			if(filterInfo == null){
				continue;
			}
			//执行第一个filter
			if(tempFilterName == null){
				sql.append(addFilterPrepend);
				sql.append(" (");
				tempFilterName = filter.getFn();
				first = true;
			}
			paramMap = new HashMap<String, Object>();
			paramMap.put("first", first);
			paramMap.put("filter", filter);
			paramMap.put("filterInfo", filterInfo);
			paramMap.put("tempFilterName", tempFilterName);
			paramMap.put("sql", sql);
			//获取每个过滤的sql
			first = getFIlterSql(paramMap);
			tempFilterName = (String) paramMap.get("tempFilterName");
		}
		if(sql.length()==0){
			return "";
		}
		return sql.append(" )").append(ele.getAttribute("suffix")).toString();

	}

	
	private boolean getFIlterSql(Map<String, Object> paramMap){
		boolean first = (Boolean) paramMap.get("first");
		FilterVO filter =  (FilterVO) paramMap.get("filter");
		NamedNodeMap filterInfo =  (NamedNodeMap) paramMap.get("filterInfo");
		String tempFilterName=  (String) paramMap.get("tempFilterName");
		StringBuilder sql=  (StringBuilder) paramMap.get("sql");
		
		Node tempNode = null;
		tempNode = filterInfo.getNamedItem("dataType");
		// 获取XML文件中配置的信息
		String dataType = tempNode==null?"VARCHAR":tempNode.getNodeValue();
		tempNode = filterInfo.getNamedItem("prepend");
		String prepend = tempNode==null?"":tempNode.getNodeValue();
		String field = filterInfo.getNamedItem("field").getNodeValue();
		tempNode = filterInfo.getNamedItem("suffix");
		String suffix = tempNode==null?"":tempNode.getNodeValue();
		// 获取当前待过滤数据的过滤类型
		int filterType = Integer.parseInt(filter.getFt());
		//如果filter子节点是addFilter标签第一条子节点，那默认建立sql时不加入filter标签的 "prepend"属性
		//如： prepend ="or"等
		if(!tempFilterName.equals(filter.getFn())){
			//多个值中的第一次 如果不是第一次 需要加 关系
			first = true;
			sql.append(") ");
			paramMap.put("tempFilterName", filter.getFn());
			sql.append(" " + prepend+" (");
		}else if(!first){
			sql.append(getRelationStr(filter.getFr()));
		}
		//判断如果是mysql,则替换使用另外的负责类进行SQL拼接 wx244051 2016-02-15
		if (databaseId.equals(DBType.MYSQL)) {
			buildMySqlFilter(filter, sql, dataType, field, filterType);
		} else if(databaseId.equals(DBType.ORACLE)){
			buildOracelFilter(filter, sql, dataType, field, filterType);
		} else {
			throw new InvalidDataAccessResourceUsageException("暂不支持当前数据库产品："+databaseId); 
		}
		sql.append(suffix);
		paramMap.put("first", false);
		return false;
	}


	private void buildOracelFilter(FilterVO filter, StringBuilder sql, String dataType, String field, int filterType) {
		if(filterType == FilterBuildSqlUtils.NOT_EQUALS || filterType == FilterBuildSqlUtils.NOT_INCLUDE){
			sql.append("(");
		}
		//sql.append("LOWER(").append(field).append(")") 
		sql.append(FilterBuildSqlUtils.filterTypeMap.get(
						filterType).buildSqlFragment(field, dataType,
						filter.getFv()));
		if(filterType == FilterBuildSqlUtils.NOT_EQUALS || filterType == FilterBuildSqlUtils.NOT_INCLUDE){
			sql.append(" or ");
			sql.append(FilterBuildSqlUtils.filterTypeMap.get(
					FilterBuildSqlUtils.NULL).buildSqlFragment(field, dataType,
					filter.getFv()));
			sql.append(")");
		}
	}


	private void buildMySqlFilter(FilterVO filter, StringBuilder sql, String dataType, String field, int filterType) {
		if(filterType == FilterBuildMySqlUtils.NOT_EQUALS || filterType == FilterBuildMySqlUtils.NOT_INCLUDE){
			sql.append("(");
		}
		//sql.append("LOWER(").append(field).append(")") 
		sql.append(FilterBuildMySqlUtils.filterTypeMap.get(
						filterType).buildSqlFragment(field, dataType,
						filter.getFv()));
		if(filterType == FilterBuildMySqlUtils.NOT_EQUALS || filterType == FilterBuildMySqlUtils.NOT_INCLUDE){
			sql.append(" or ");
			sql.append(FilterBuildMySqlUtils.filterTypeMap.get(
					FilterBuildMySqlUtils.NULL).buildSqlFragment(field, dataType,
					filter.getFv()));
			sql.append(")");
		}
	}
	
	
	
	private Map<String, NamedNodeMap>  getFilterAttrMap(Element ele){
		// 解析filter属性,组织filter属性Map，filter Name作为key
		NodeList nodeList = ele.getElementsByTagName("filter");
		int len = nodeList.getLength();
		Map<String, NamedNodeMap> filterAttrMap = new HashMap<String, NamedNodeMap>();
		for (int i = 0; i < len; i++) {
			// 获取filter标签的属性信息
			Node node = nodeList.item(i);
			NamedNodeMap attrs = node.getAttributes();
			String nameAttr = attrs.getNamedItem("name").getNodeValue();
			filterAttrMap.put(nameAttr, attrs);
		}
		return filterAttrMap;
	}
	/**
	 * 返回字符串
	 * @param relation
	 * @return
	 * Created：2013-8-19
	 * @author chaowangbang
	 */
	private String getRelationStr(String relation){
		if("1".equals(relation)){
			return " OR ";
		}
		return " AND ";
	}
	
	/**
	 * 判断是否有 pageVO 参数 并返回对象
	 * 
	 * @param parameter
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<FilterVO> findFilter(Object parameter) {
		if (null == parameter) {
			return null;
		}
		PageVO page = null;
		// 这个的情况比较多 优先执行这个代码
		if (parameter instanceof Map) {
			Map<Object, Object> parameterMap = (Map<Object, Object>) parameter;
			for (Map.Entry<Object, Object> entry : parameterMap.entrySet()) {
				if (entry.getValue() instanceof PageVO) {
					page = (PageVO) entry.getValue();
					return page.getFilters();
				}
			}
		}

		// 后面再考虑单个参数的问题
		if (parameter instanceof PageVO) {
			page = (PageVO) parameter;
			return page.getFilters();
		}
		return null;
	}
	
}
